import datetime
from collections import namedtuple

import jwt

from user.models import User
from django.conf import settings
from django.db.models import Model
from django.http import JsonResponse
from django.http.request import QueryDict
from django.db.models.options import Options
from django.db.models.manager import Manager

KEY = settings.SECRET_KEY
EXP = 3600 * 24 * 7  # 7天过期


def get_token(user: Model):
    payload = jsonify(user, exclude='password')
    now = datetime.datetime.now().timestamp()
    payload.update({'exp': now + EXP})
    token = jwt.encode(payload, KEY).decode()
    return token


def jsonify(instance, allow=None, exclude=None):
    if exclude is None:
        exclude = ()
    # 如果只有一个的exclude的时候，支持直接写那个字段 如jsonify(user, exclude='password')
    if isinstance(exclude, str):
        exclude = (exclude,)

    model: Options = getattr(type(instance), '_meta')

    if allow:
        # 如果只有一个的allow的时候，支持直接写那个字段 如jsonify(user, allow='id')
        if isinstance(allow, str):
            return {allow: getattr(instance, allow)}
        return {field: getattr(instance, field) for field in allow}

    return {field.name: getattr(instance, field.name) for field in model.fields if field.name not in exclude}


def auth_decorator(fn):
    def _wrapper(*args, **kwargs):
        request = args[1] if len(args) > 1 else args[0]
        try:
            token = request.META.get('HTTP_TOKEN')
            payload = jwt.decode(token, KEY)
        except Exception as e:
            print(e)
            return JsonResponse({'error': '认证失败'}, status=401)
        if len(args) > 1:
            setattr(args[0], 'payload', payload)
        return fn(*args, **kwargs)

    return _wrapper


def query_get(query_dict: QueryDict, allow=None, *, type_func=lambda x: x, **kwargs):
    """
    从查询字符串中得到想要的值

    :param query_dict: request.GET
    :param allow: 一个字典对象，字典的key为需要的值，key对应的value为缺省值
    :param type_func: 设置所有的值的类型，当参数多，类型杂的时候极其不好用
    :return: 需要的字典对象
    """
    if allow is None:
        allow = {}
    allow.update(kwargs)
    Obj = namedtuple('Obj', allow.keys())

    return Obj(*[type_func(query_dict.get(k, v)) for k, v in allow.items()])


def check_value(value, value_range, type_func=int):
    """
    为value进行检查校验，并进行类型转换

    :param value: 待校验的值
    :param value_range: 一个字符串，例如 "1-10" 则将会判断此值是否在这个范围内，如果不在将此值置为范围的最小值
    :param type_func: 确保最终的值为此类型
    :return: 如果value在期待的范围内且类型符合则直接返回，否则将转换后的值返回
    """
    if '-' in value_range:
        start, end = map(type_func, value_range.split('-'))
    else:
        start = end = type_func(value_range)
    try:
        if not isinstance(value, type_func):
            value = type_func(value)
        if start <= value <= end:
            return value
    except Exception as e:
        print(e)
    return start

